package nqueens.algorithm;

import nqueens.chessStuff.*;

public class DrGinatAlgorithm extends Algorithm {

    /**
     * Constructor that sets the flag for validating solutions.
     * @param validateSolutions whether or not to validate solutions.
     */
    public DrGinatAlgorithm (boolean validateSolutions) {
        super (validateSolutions);
    }

    /**
     * Helper method that solves for solutions
     * @param n the size of the chessboard
     * @throws InterruptedException
     */
    protected void solve (int n) throws InterruptedException {
        setChessBoard(new ChessBoard(n,isValidatingSolutions()));
        // switch statement to determine which form N is in
        switch (n%6) {
            // this corresponds to when N is in the form 6K-2 or 6K
            case 0:
            case 4:
                setChessBoard(case2(n));
                validateSolution();
                setLastSolutionSolved(getChessBoard());
                break;
            // this corresponds to when N is in the form 6K-1 or 6K+1
            case 1:
            case 5:
                setChessBoard(case1(n));
                validateSolution();
                setLastSolutionSolved(getChessBoard());
                break;
            // this corresponds to when N is in the form 6K+2
            case 2:
                setChessBoard(case3(n));
                validateSolution();
                setLastSolutionSolved(getChessBoard());
                break;
            // this corresponds to when N is in the form 6K+3
            case 3:
                setChessBoard(case4(n));
                validateSolution();
                setLastSolutionSolved(getChessBoard());
                break;
            default:
                System.err.print("This line of text means there is an error in the solve method of DrGinatAlgorithm.\n");
        }
    }

    /**
     * Helper method for case 1.  This corresponds to when N is in the form 6K-1 or 6K+1.
     * @param n The problem size being solved for this case
     * @return The Chessboard after having been solved for this size problem set (ie n)
     * @throws InterruptedException Caught interruption from main thread
     */
    private ChessBoard case1 (int n) throws InterruptedException {
        ChessBoard cb = new ChessBoard (n,isValidatingSolutions());
        for (int row = 0, col = 0, wraps = 0; row < n; row++, col+=2) {
            if (col > cb.getN() - 1) {
                wraps++;
                col = wraps;
            }
            cb.placeQueen(row, col);
        }
        return cb;
    }

    /**
     * Helper method for case 2.  This corresponds to when N is in the form 6K-2 or 6K.
     * @param n The problem size being solved for this case
     * @return The Chessboard after having been solved for this size problem set (ie n)
     * @throws InterruptedException Caught interruption from main thread
     */
    private ChessBoard case2 (int n) throws InterruptedException {
        ChessBoard properBoard = new ChessBoard (n,isValidatingSolutions());
        // solve for case 1 with one extra row/column, then chop off the top row and left column
        // by translating the queen postions for all but the original top row queen
        for (Queen q : case1(n + 1).getQueenSet() ) {
            if (q.getSquare().getRow() != 0) {
                properBoard.placeQueen(q.getSquare().getRow() - 1, q.getSquare().getColumn() - 1);
            }
        }
        return properBoard;
    }

    /**
     * Helper method for case 3.  This corresponds to when N is in the form 6K+2.
     * @param n The problem size being solved for this case
     * @return The Chessboard after having been solved for this size problem set (ie n)
     * @throws InterruptedException Caught interruption from main thread
     */
    private ChessBoard case3 (int n) throws InterruptedException {
        ChessBoard cb = case1(n);
        Queen firstQueen = cb.getOneQueen(0,0);
        Queen lastQueen = cb.getOneQueen(n-1,n-1);
        int firstQueenSwapRow = ((n-2)/6) *3 - 1;
        int lastQueenSwapRow = n - (firstQueenSwapRow) - 1;

        Queen swapQueen;
        int columnCounter;
        for (columnCounter = 0; !cb.getChessBoardSquare(firstQueenSwapRow, columnCounter).isOccupied(); columnCounter++);
        swapQueen = cb.getOneQueen(firstQueenSwapRow, columnCounter);

        // swap the first queen and the one in the row firstQueenSwapRow
        cb.removeQueen(firstQueen);
        cb.placeQueen(firstQueenSwapRow, 0);
        cb.removeQueen(swapQueen.getSquare().getRow(), swapQueen.getSquare().getColumn());
        cb.placeQueen(0, columnCounter);

        for (columnCounter = 0; !cb.getChessBoardSquare(lastQueenSwapRow, columnCounter).isOccupied(); columnCounter++);
        swapQueen = cb.getOneQueen(lastQueenSwapRow, columnCounter);
        // swap the last queen and the one in the row lastQueenSwapRow
        cb.removeQueen(lastQueen);
        cb.placeQueen(lastQueenSwapRow, n-1);
        cb.removeQueen(swapQueen.getSquare().getRow(), swapQueen.getSquare().getColumn());
        cb.placeQueen(n-1, columnCounter);

        return cb;
    }

    /**
     * Helper method for case 4.  This corresponds to when N is in the form 6K+3.
     * @param n The problem size being solved for this case
     * @return The Chessboard after having been solved for this size problem set (ie n)
     * @throws InterruptedException Caught interruption from main thread
     */
    private ChessBoard case4 (int n) throws InterruptedException {
        ChessBoard cb = new ChessBoard(n,isValidatingSolutions());
        cb.placeQueen(0, 0); // place the first queen on the main diagonal
        for (Queen q : case3(n - 1).getQueenSet()) {
            cb.placeQueen(q.getSquare().getRow() + 1, q.getSquare().getColumn() + 1);

        }
        return cb;
    }
}
